
package com.vanda.db;

import java.io.Serializable;
import java.util.List;

import net.sf.hibernate.HibernateException;
import net.sf.hibernate.Query;
import net.sf.hibernate.Session;
import net.sf.hibernate.StaleObjectStateException;
import net.sf.hibernate.type.Type;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.vanda.data.AbstractData;
import com.vanda.data.AbstractSelectData;
import com.vanda.data.BeanListBase;
import com.vanda.db.cfg.MappingInfo;
import com.vanda.db.exception.AsynchronousException;
import com.vanda.db.exception.CreateException;
import com.vanda.db.exception.DbException;
import com.vanda.db.util.DBUtils;
import com.vanda.db.util.XQLParser;

/**
 * HQL Accessor访问类 <br>
 * 使用Hibernate
 * @author 王瑛<br>
 * @version Version 1.00<br>
 */
public class HQLAccessor extends Accessor {
	/** 取得日志类 */
	private static Log log = LogFactory.getLog(HQLAccessor.class);

	/** 构造方法 */
	HQLAccessor(Session session) {
		super(session);
	}

	/**
	 * 新增一条记录
	 * 
	 * @param dataBean
	 *            a transient instance of a persistent class
	 * @throws DbException
	 */
	public void insert(AbstractData dataBean) throws DbException {
		if(dataBean == null){
			return ;
		}
		try {
			if (!session.contains(dataBean)) {
				session.save(dataBean);
			}
		} catch (StaleObjectStateException ae) {
			ae.printStackTrace();
			throw new AsynchronousException(ae);
		} catch (HibernateException he) {
			he.printStackTrace();
			throw new DbException(he);
		}
	}

	/**
	 * 新增多条记录
	 * 
	 * @param lstBean
	 * @throws DbException
	 */
	public void insert(List lstBean) throws DbException {
		if(lstBean == null){
			return ;
		}
		for (int i = 0; i < lstBean.size(); i++) {
			AbstractData dataBean = (AbstractData) lstBean.get(i);
			insert(dataBean);
		}
	}

	/**
	 * 修改一条记录 If there is a persistent instance with the same identifier, an
	 * exception is thrown.
	 * 
	 * @param dataBean
	 *            a transient instance containing updated state
	 * @throws DbException
	 */
	public void update(AbstractData dataBean) throws DbException {
		if(dataBean == null){
			return ;
		}
		try {
			if (!session.contains(dataBean)) {
				session.update(dataBean);
			}
		} catch (StaleObjectStateException ae) {
			ae.printStackTrace();
			throw new AsynchronousException(ae);
		} catch (HibernateException he) {
			he.printStackTrace();
			throw new DbException(he);
		}
	}

	/**
	 * 修改多条记录
	 * 
	 * @param lstBean
	 * @throws DbException
	 */
	public void update(List lstBean) throws DbException {
		if(lstBean == null){
			return ;
		}
		for (int i = 0; i < lstBean.size(); i++) {
			AbstractData dataBean = (AbstractData) lstBean.get(i);
			update(dataBean);
		}
	}

	/**
	 * 新增或修改一条记录,如果没有设置主键，则新增，如果有，则修改！<p>
	 * depending upon the value of its identifier property. <br>
	 * By default the instance is always saved. <br>
	 * This behaviour may be adjusted by specifying an unsaved-value attribute of the
	 * identifier property mapping.
	 * 
	 * @param dataBean
	 *            a transient instance containing new or updated state
	 * @throws DbException
	 */
	public void save(AbstractData dataBean) throws DbException {
		if(dataBean == null){
			return ;
		}
		try {
			if (!session.contains(dataBean)) {
				session.saveOrUpdate(dataBean);
			}
		} catch (StaleObjectStateException ae) {
			ae.printStackTrace();
			throw new AsynchronousException(ae);
		} catch (HibernateException he) {
			he.printStackTrace();
			throw new DbException(he);
		}
	}

	/**
	 * 新增或修改多条记录
	 * 
	 * @param lstBean
	 * @throws DbException
	 */
	public void save(List lstBean) throws DbException {
		if(lstBean == null){
			return ;
		}
		for (int i = 0; i < lstBean.size(); i++) {
			AbstractData dataBean = (AbstractData) lstBean.get(i);
			save(dataBean);
		}
	}

	/**
	 * 删除一条记录 <p>
	 * Remove a persistent instance from the datastore. <br>
	 * The argument may be an instance associated with the receiving Session or a transient
	 * instance with an identifier associated with existing persistent state.
	 * 
	 * @param obj
	 *            the instance to be removed
	 * @throws DbException
	 */
	public void delete(AbstractData dataBean) throws DbException {
		if(dataBean == null){
			return ;
		}
		try {
			session.delete(dataBean);
		} catch (StaleObjectStateException ae) {
			ae.printStackTrace();
			throw new AsynchronousException(ae);
		} catch (HibernateException he) {
			he.printStackTrace();
			throw new DbException(he);
		}
	}

	/**
	 * 删除多条记录
	 * 
	 * @param lstBean
	 * @throws DbException
	 */
	public void delete(List lstBean) throws DbException {
		if(lstBean == null){
			return ;
		}
		for (int i = 0; i < lstBean.size(); i++) {
			AbstractData dataBean = (AbstractData) lstBean.get(i);
			delete(dataBean);
		}
	}

	/**
	 * 删除一条记录
	 * @param clazz 待删除类Class属性
	 * @param id 主键ID
	 * @throws DbException
	 */
	public void delete(Class clazz, Serializable id) throws DbException {
		try {
			Object obj = session.get(clazz, id);
			session.delete(obj);
		} catch (StaleObjectStateException ae) {
			ae.printStackTrace();
			throw new AsynchronousException(ae);
		} catch (HibernateException he) {
			he.printStackTrace();
			throw new DbException(he);
		}
	}

	/**
	 * 删除符合条件的记录 <p>
	 * Delete all objects returned by the query. Return the number of
	 * objects deleted.
	 * 
	 * @param query
	 *            the query string
	 * @throws DbException
	 */
	public int delete(String sql) throws DbException {
		try {
			return session.delete(sql);
		} catch (StaleObjectStateException ae) {
			ae.printStackTrace();
			throw new AsynchronousException(ae);
		} catch (HibernateException he) {
			he.printStackTrace();
			throw new DbException(he);
		}
	}

	/**
	 * 返回给定标识符的实例，如果数据库不存在，则返回null <p>
	 * Return the persistent instance of the given
	 * entity class with the given identifier, <br>or null if there is no such
	 * persistent instance
	 * 
	 * @param clazz
	 *            实例类型
	 * @param id
	 *            标识符
	 * @return a persistent instance or null
	 * @throws DbException
	 */
	public AbstractData get(Class clazz, Serializable id) throws DbException {
		try {
			Object obj = session.get(clazz, id);
			if (obj != null) {
				return (AbstractData) obj;
			}
			return null;
		} catch (HibernateException he) {
			he.printStackTrace();
			throw new DbException(he);
		}
	}

	/**
	 * 返回给定vo的po，data必须设置id
	 * 
	 * @param data
	 * @return
	 * @throws DbException
	 */
	public AbstractData get(AbstractData data) throws DbException {
		if (data.getId() == null) {
			return null;
		} else {
			return get(data.getClass(), data.getId());
		}
	}

	/**
	 * 执行查询操作
	 * 
	 * @param query
	 *            a query expressed in Hibernate's query language
	 * @return a distinct list of instances (or arrays of instances)
	 * @throws DbException
	 */
	private BeanListBase query(String hql) throws DbException {
		try {
			Query q = session.createQuery(hql);
			q.setMaxResults(super.MAXLENGTH);

			List lstResult = q.list();
			//如果查询长度＝系统最大长度，则抛出异常
			checkQueryLimit(lstResult.size(),hql);
			return new BeanListBase(lstResult);
		} catch (HibernateException he) {
			he.printStackTrace();
			throw new DbException(he);
		} catch (DbException he) {
			he.printStackTrace();
			throw he;
		} catch (Exception he) {
			he.printStackTrace();
			throw new DbException(he);
		}
	}

	/**
	 * 执行带参数的查询操作
	 * 
	 * @param hql
	 *            查询语句
	 * @param lstValue
	 *            查询参数集合
	 * @return
	 * @throws DbException
	 */
	public BeanListBase query(String hql, List lstValue) throws DbException {
		try {
			//LogFunc.info("HQL:" + hql);
			if (lstValue == null || lstValue.size() == 0) {
				return query(hql);
			}
			
			Query q = session.createQuery(hql);
			q.setMaxResults(super.MAXLENGTH);
			Object[] values = null;
			Type[] types = null;

			values = lstValue.toArray();
			types = (Type[]) DBUtils
					.convert(values, DBUtils.HIBERNATE_TYPE);
			for (int i = 0; i < values.length; i++) {
				q.setParameter(i, values[i], types[i]);
			}
			
			List lstResult = q.list();
			//如果查询长度＝系统最大长度，则抛出异常
			checkQueryLimit(lstResult.size(),hql);
			
			return new BeanListBase(lstResult);
		} catch (HibernateException he) {
			he.printStackTrace();
			throw new DbException(he);
		} catch (DbException he) {
			he.printStackTrace();
			throw he;
		} catch (Exception he) {
			he.printStackTrace();
			throw new DbException(he);
		}
	}

	/**
	 * 翻页查询，需要设置dataBean的pageNo（当前页）属性, 返回后的dataBean会设置recordCount（总记录数）属性
	 * 
	 * @param hql
	 *            查询语句
	 * @param lstValue
	 *            查询参数集合
	 * @param dataBean
	 *            翻页查询设置
	 * @return
	 * @throws DbException
	 */
	private BeanListBase query(String hql, List lstValue,
			AbstractSelectData dataBean) throws DbException {
		if (dataBean.getPageNo() <= 0 || dataBean.getPageRowNum() <= 0) {
			return query(hql, lstValue);
		}
		//根据sql得到对应的查询记录总数的sql
		String countSql = XQLParser.getCountXQL(hql);
		return query(hql, countSql, lstValue, dataBean);
	}

	/**
	 * 翻页查询，dataBean保存翻页设置，使用用户设置的查询总数sql.
	 * 
	 * @param hql
	 *            查询语句
	 * @param countHQL
	 *            查询总数hql
	 * @param lstValue
	 *            参数设置
	 * @param dataBean
	 *            翻页设置
	 * @return
	 * @throws DbException
	 */
	private BeanListBase query(String hql, String countHQL, List lstValue,
			AbstractSelectData dataBean) throws DbException {

		if (dataBean.getPageNo() <= 0 || dataBean.getPageRowNum() <= 0) {
			return query(hql, lstValue);
		}

		
		List lstResult = null;
		try {
			//查询起点位置
			int first = (dataBean.getPageNo() - 1) * (dataBean.getPageRowNum());
			//如果dataBean.getPageNo()的值<0,则从第一页开始
			first = first < 0 ? 1 : first;
			//查询最大数量
			int maxResult = dataBean.getPageRowNum();
			//查询符合范围的数据
			Query q = session.createQuery(hql);
			q.setFirstResult(first);
			q.setMaxResults(maxResult);
			Object[] values = null;
			Type[] types = null;
			if (lstValue != null && lstValue.size() > 0) {
				values = lstValue.toArray();
				types = (Type[]) DBUtils
						.convert(values, DBUtils.HIBERNATE_TYPE);
				for (int i = 0; i < values.length; i++) {
					q.setParameter(i, values[i], types[i]);
				}
			}
			lstResult = q.list();
			//if (lstResult.size() > 0) {
			//查询记录总数
			int count = 0;
			if (values == null) {
				count = ((Integer) session.iterate(countHQL).next()).intValue();
			} else {
				count = ((Integer) session.iterate(countHQL, values, types)
						.next()).intValue();
			}
			dataBean.setRecordCount(count);
			//} else {
			//	dataBean.setRecordCount(0);
			//}
		} catch (HibernateException he) {
			he.printStackTrace();
			throw new DbException(he);
		} catch (Exception he) {
			he.printStackTrace();
			throw new DbException(he);
		}
		BeanListBase beanList = new BeanListBase(lstResult);
		beanList.setSelectData(dataBean);
		return beanList;
	}

	/**
	 * 返回查询结果第一条记录,如果无结果，则返回null
	 * 
	 * @param hql
	 * @return
	 * @throws DbException
	 */
	private Object find(String hql) throws DbException {

		Object first = null;
		try {
			List lstResult = query(hql).getReferenceObject();
			if (lstResult.size() > 0) {
				first = lstResult.get(0);
			}
		} catch (DbException he) {
			throw he;
		} catch (Exception he) {
			he.printStackTrace();
			throw new DbException(he);
		}
		return first;
	}

	/**
	 * 带条件参数查询
	 * 
	 * @param hql Hibernate HQL
	 * @param lstValue 查询条件参数
	 * @return
	 * @throws DbException
	 */
	private Object find(String hql, List lstValue) throws DbException {
		Object first = null;
		try {
			
			List lstResult = query(hql, lstValue).getReferenceObject();
			if (lstResult.size() > 0) {
				first = lstResult.get(0);
			}
		} catch (DbException he) {
			throw he;
		} catch (Exception he) {
			he.printStackTrace();
			throw new DbException(he);
		}
		return first;
	}

	/**
	 * 强迫提交数据库 <P>
	 * Must be called at the end of a unit of work, before commiting the
	 * transaction and closing the session (Transaction.commit() calls this
	 * method). Flushing is the process of synchronising the underlying
	 * persistent store with persistable state held in memory.
	 * 
	 * @throws DbException
	 */
	public void flush() throws DbException {
		try {
			session.flush();
		} catch (StaleObjectStateException ae) {
			ae.printStackTrace();
			throw new AsynchronousException(ae);
		} catch (HibernateException he) {
			he.printStackTrace();
			throw new DbException(he);
		}
	}

	/**
	 * 查询xml配置的hql,返回第一条记录
	 * @param name HQL名称
	 * @param dataBean 保存参数的DTO实例
	 * @return 查询结果,返回查询结果第一条记录,没有查询结果则返回null.
	 */
	public Object findName(String name, Object dataBean)
			throws DbException {
		BeanListBase beanList = queryName(name, dataBean);
		List resultList = beanList.getReferenceObject();
		if (resultList != null && resultList.size() > 0) {
			return resultList.get(0);
		}
		return null;	
	}
	
	/**
	 * 查询xml配置的hql
	 * 
	 * @param name HQL名称
	 * @param dataBean 查询参数，如果有多个，用Object[]传递，而不要用Collection接口类
	 * @return
	 */
	public BeanListBase queryName(String name, Object dataBean)
			throws DbException {
		Object[] dataBeans = null;
		//如果是数组
		if (dataBean instanceof Object[]) {
			dataBeans = (Object[]) dataBean;
		} else {
			dataBeans = new Object[1];
			dataBeans[0] = dataBean;
		}
		return queryName(name, dataBeans);
	}

	/**
	 * 将HQLAccessor适配成SQLAccessor，即共用一个session。<p>
	 * 在一个事务里想同时操作hql和sql时使用，最后关闭时只调用其中一个Accessor.close()方法
	 * 
	 * @return
	 * @throws CreateException
	 */
	public SQLAccessor adapter() throws CreateException {
		if (super.adaptAccessor == null) {
			super.adaptAccessor = DBFactory.getAccessor(super.getSession(),
					DBFactory.ACCESSOR_TYPE_SQL);
			super.adaptAccessor.adaptAccessor = this;
		}

		return (SQLAccessor) super.adaptAccessor;
	}

	/**
	 * 查询xml配置的hql
	 * 
	 * @param name HQL名称
	 * @param dataBeans 查询参数
	 * @return
	 */
	private BeanListBase queryName(String name, Object[] dataBeans)
			throws DbException {
		BeanListBase lstResult = null;
		try {
			MappingInfo info = super.findMapping(name);
			XQLData[] datas = XQLParser.parse(info, dataBeans);
			XQLData countData = null;

			//是否翻页
			boolean bPage = false;
			AbstractSelectData selectData = null;
			if(	dataBeans[0] instanceof AbstractSelectData) {
				selectData = (AbstractSelectData)dataBeans[0];
				if(selectData.getPageNo() > 0){
					bPage = true;
				}
			}
			String countHQL = null;
			//如果是翻页，检查是否有Count HQL
			if (bPage == true) {
				countData = XQLParser.parseCountXQL(info, dataBeans);
				if(countData != null)
					countHQL = countData.getXql();
			}
			//翻页查询
			if (bPage) {
				if (countHQL != null && countHQL.trim().length() > 0) {
					lstResult = this.query(datas[0].getXql(), countHQL,
							datas[0].getValues(),
							(AbstractSelectData) dataBeans[0]);
				} else {
					lstResult = this.query(datas[0].getXql(), datas[0]
							.getValues(), (AbstractSelectData) dataBeans[0]);
				}
			} else { //不做翻页查询
				lstResult = this.query(datas[0].getXql(), datas[0].getValues());
			}
		} catch (DbException e) {
			throw e;
		} catch (Exception e) {
			throw new DbException(e);
		}
		return lstResult;
	}

}